#pragma once

using namespace System;
using namespace NUnit::Framework;
using namespace LatoolNet;

namespace LatoolNetTest {

	[TestFixture]
	public ref class DoubleGeneralMatrixTest {
	public:
		[Test]
		void TestSetGetValue() {

			int rownum = 4;
			int colnum = 4;

			array<double, 2>^ expected = gcnew array<double, 2>(rownum, colnum);

			Matrix ^ actual = gcnew Matrix(rownum, colnum);

			Assert::AreEqual(MatrixType::DoubleGeneral, actual->Type, "Test: Type");

			Random ^ randomgen = gcnew Random();
			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					expected[i, j] = randomgen->NextDouble() * 10 - 5;
				}
			}

			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					actual[i, j] = expected[i, j];
				}
			}

			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					Assert::AreEqual(expected[i, j], actual[i, j], 1e-10, "Test: Set/Get value");
				}
			}

		}

		[Test]
		void TestCopyInvMultiplyMatrix() {
			
			int rownum = 4;
			int colnum = 4;

			Matrix ^ a = gcnew Matrix(rownum, colnum);
			
			Random ^ randomgen = gcnew Random();
			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					a[i, j] = randomgen->NextDouble() * 10 - 5;
				}
			}
			Matrix ^ ainv = a->Clone()->Inv();

			Matrix ^ actual = a * ainv;
			
			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					if (i == j) {
						Assert::AreEqual(1.0, actual[i, j], 1e-10, "Test: inv");						
					} else {
						Assert::AreEqual(0.0, actual[i, j], 1e-10, "Test: inv");						
					}
				}
			}


		};

		[Test]
		void TestInvertMatrix() {
			
			int rownum = 4;
			int colnum = 4;

			Matrix ^ a = gcnew Matrix(rownum, colnum);
			
			Random ^ randomgen = gcnew Random();
			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					a[i, j] = randomgen->NextDouble() * 10 - 5;
				}
			}
			Matrix ^ ainv = a->Clone()->Inv();

			Matrix ^ ainvinv =ainv->Clone()->Inv();
			
			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					Assert::AreEqual(a[i, j], ainvinv[i, j], 1e-10, "Test: inv of inv");						
				}
			}


		};


		[Test]
		void TestFactorizedSolve() {
			
			int rownum = 4;
			int colnum = 4;

			Matrix ^a = gcnew Matrix(rownum, colnum);
			Random ^ randomgen = gcnew Random();
			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					a[i, j] = randomgen->NextDouble() * 10 - 5;
				}
			}

			Matrix ^expected = gcnew Matrix(rownum, 1);

			for (int i = 0; i < rownum; i++) {
				double d = randomgen->NextDouble() * 10 - 5;				
				expected[i, 0] = d;
			}

			Matrix ^ b = a * expected;

			//LUFactorization::Factorize(a);
			//LUFactorization::Solve(a, b);
			LinearEquation::Factorize(a);
			LinearEquation::Solve(a, b);

			for (int i = 0; i < rownum; i++) {
				Assert::AreEqual(expected[i, 0], b[i, 0], 1e-10, "Test: Factorized Solve.");
			}


		};

		[Test]
		void TestSolve() {
			
			int rownum = 4;
			int colnum = 4;

			Matrix ^a = gcnew Matrix(rownum, colnum);
			Random ^ randomgen = gcnew Random();
			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					a[i, j] = randomgen->NextDouble() * 10 - 5;
				}
			}

			Matrix ^expected = gcnew Matrix(rownum, 1);

			for (int i = 0; i < rownum; i++) {
				expected[i, 0] = randomgen->NextDouble() * 10 - 5;	
			}

			Matrix ^ b = a * expected;

			//LUFactorization::Solve(a, b);
			LinearEquation::Solve(a, b);

			for (int i = 0; i < rownum; i++) {
				Assert::AreEqual(expected[i, 0], b[i, 0], 1e-10, "Test: Factorized Solve.");
			}


		};

		[Test]
		void TestRepeatedFactorizedSolve() {

			int rownum = 4;
			int colnum = 4;

			Matrix ^a = gcnew Matrix(rownum, colnum);
			Random ^ randomgen = gcnew Random();
			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					a[i, j] = randomgen->NextDouble() * 10 - 5;
				}
			}

			Matrix ^expected = gcnew Matrix(rownum, 1);
			for (int i = 0; i < rownum; i++) {
				expected[i, 0] = randomgen->NextDouble() * 10 - 5;	
			}

			Matrix ^ b = a * expected;

			Matrix^ expected2 = gcnew Matrix(rownum, 1);
			for (int i = 0; i < rownum; i++) {
				expected2[i, 0] = randomgen->NextDouble() * 10 - 5;	
			}

			Matrix^ b2 = a * expected2;

			//LUFactorization::Factorize(a);
			LinearEquation::Factorize(a);

			//LUFactorization::Solve(a, b);
			LinearEquation::Solve(a, b);

			//LUFactorization::Solve(a, b2);
			LinearEquation::Solve(a, b2);

			for (int i = 0; i < rownum; i++) {
				Assert::AreEqual(expected[i, 0], b[i, 0], 1e-10, "Test: Repeated Factorized Solve.");
			}

			for (int i = 0; i < rownum; i++) {
				Assert::AreEqual(expected2[i, 0], b2[i, 0], 1e-10, "Test: Repeated Factorized Solve.");
			}

		};


		[Test]
		void TestRepeatedSolve() {

			int rownum = 4;
			int colnum = 4;

			Matrix ^a = gcnew Matrix(rownum, colnum);
			Random ^ randomgen = gcnew Random();
			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					a[i, j] = randomgen->NextDouble() * 10 - 5;
				}
			}

			Matrix ^expected = gcnew Matrix(rownum, 1);
			for (int i = 0; i < rownum; i++) {
				expected[i, 0] = randomgen->NextDouble() * 10 - 5;	
			}

			Matrix ^ b = a * expected;

			Matrix^ expected2 = gcnew Matrix(rownum, 1);
			for (int i = 0; i < rownum; i++) {
				expected2[i, 0] = randomgen->NextDouble() * 10 - 5;	
			}

			Matrix^ b2 = a * expected2;

			//LUFactorization::Solve(a, b);
			LinearEquation::Solve(a, b);

			//LUFactorization::Solve(a, b2);
			LinearEquation::Solve(a, b2);

			for (int i = 0; i < rownum; i++) {
				Assert::AreEqual(expected[i, 0], b[i, 0], 1e-10, "Test: Repeated Solve.");
			}

			for (int i = 0; i < rownum; i++) {
				Assert::AreEqual(expected2[i, 0], b2[i, 0], 1e-10, "Test: Repeated Solve.");
			}

		};

	};
}
